package com.scs.application.modules.wm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.scs.application.consts.GlobalConsts;
import com.scs.application.consts.SerialKeys;
import com.scs.application.core.exception.BusinessException;
import com.scs.application.core.flow.FlowContext;
import com.scs.application.core.service.BaseServiceImpl;
import com.scs.application.core.utils.StringUtils;
import com.scs.application.core.utils.UserUtils;
import com.scs.application.core.utils.UtilJava8;
import com.scs.application.core.utils.UtilNum;
import com.scs.application.modules.base.entity.Matr;
import com.scs.application.modules.base.mapper.MatrMapper;
import com.scs.application.modules.wm.dto.WorkOrderKey;
import com.scs.application.modules.wm.entity.*;
import com.scs.application.modules.wm.enums.WorkOrderType;
import com.scs.application.modules.wm.mapper.InOrderMapper;
import com.scs.application.modules.wm.mapper.StockMapper;
import com.scs.application.modules.wm.mapper.WorkOrderItemMapper;
import com.scs.application.modules.wm.mapper.WorkOrderMapper;
import com.scs.application.modules.wm.service.*;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * <p>
 * 入库单 服务实现类
 * </p>
 */
@Slf4j
@Service
public class OutOrderServiceImpl extends BaseServiceImpl<InOrderMapper, WorkOrder> implements OutOrderService {

    @Autowired
    private WorkOrderItemService workOrderItemService;

    @Autowired
    private TransferItemService transferItemService;

    @Autowired
    private WorkOrderService workOrderService;

    @Autowired
    private StockMapper stockMapper;

    @Autowired
    private StockLockService stockLockService;

    @Autowired
    private WorkOrderItemMapper workOrderItemMapper;

    @Autowired
    private WorkOrderMapper workOrderMapper;

    @Autowired
    private OutOrderItemService outOrderItemService;

    @Autowired
    private MatrMapper matrMapper;

    @Autowired
    @Lazy
    private StockService stockService;;


    /**
     * 根据送调拨单转换出库单
     *
     * @param transfer
     * @return
     */
    public List<WorkOrder> createWorkOrderByTransfer(Transfer transfer) {
        if (StringUtils.isBlank(transfer.getFromWarehouseId())) throw new BusinessException("调拨单【%s】缺少缺少调出仓库，请检查",transfer.getBusKey());
        if (StringUtils.isBlank(transfer.getToWarehouseId())) throw new BusinessException("调拨单【%s】缺少缺少调入仓库，请检查",transfer.getBusKey());
        if (transfer == null) {
            throw new BusinessException("不正确的调拨单");
        }

        List<TransferItem> transferItemList = transferItemService.list(new QueryWrapper<TransferItem>().eq("transfer_id", transfer.getId()));
        if (transferItemList.size() == 0) {
            throw new BusinessException("调拨单明细为空");
        }

        //根据库存里面的供应商分组，因为耗材供应商可能被修改
        List<String> stockIdList =transferItemList.stream().map(TransferItem::getStockId).collect(Collectors.toList());
        List<Stock> stockList = stockMapper.selectList(Wrappers.<Stock>query().in("id", stockIdList));
        if (stockList == null) {
            throw new BusinessException("调拨单明细所对应的库存在系统中不存在，转换出库单失败");
        }
        if (stockList.size() != transferItemList.size()) {
            throw new BusinessException("调拨单明细数量【%s】与库存记录库存数量【%s】不一致，转换出库单失败",transferItemList.size(),stockList.size());
        }


        //分组去重，条件：供应商，耗材，寄售，一物一码
        List<Stock> stockGroupList = UtilJava8.stockGroup(stockList);
        ArrayList<WorkOrder> orders = Lists.newArrayList();
        List<String> codeList = serialRuleAdapter.getSerialRuleService().batchGenerateCode(SerialKeys.OUT_ORDER_KEY,"Transfer",stockGroupList.size());
        AtomicInteger codeIndex = new AtomicInteger();
        stockGroupList.stream().forEach(stockGroup -> {
            WorkOrderKey orderKey = new WorkOrderKey(stockGroup.getSupplierId(), stockGroup.getSupplierName(), stockGroup.getFlagConsignment(), stockGroup.getFlagOmoc());

            //符合该分组的所有库存
            List<Stock> stockAddList = UtilJava8.stockFilter(stockList,stockGroup);
            WorkOrder order = convertWorkOrder(transfer,orderKey, UtilJava8.getWOTransferItems(stockAddList,transferItemList),codeList.get(codeIndex.get()));
            orders.add(order);
            codeIndex.getAndIncrement();
        });
        log.info("调拨共生成{}个出库单", orders.size());
        return orders;
    }


    private WorkOrder convertWorkOrder(Transfer transfer, WorkOrderKey workOrderKey, List<TransferItem> transferItemList,String codeOut) {
        WorkOrder workOrder = new WorkOrder();

        Double amount = transferItemList.stream().mapToDouble(TransferItem::getAmount).sum();
        workOrder.setAsnKey("")
                .setRefKey(transfer.getBusKey())
                .setSupplierId(workOrderKey.getSupplierId())
                .setSupplierName(transferItemList.get(0).getSupplierName())
                .setAmount(amount)
                .setRefId(transfer.getId()).setRefTable("wm_transfer")
                .setPurchaseOrderId(null)
                .setFlagConsignment(workOrderKey.getFlagConsignment())
                .setFlagOmoc(workOrderKey.getFlagOmoc())
                .setBusType(WorkOrderType.OUT_SHIFT)
                .setBusDate(Calendar.getInstance().getTime())
                .setDirection(GlobalConsts.ORDER_DIRECTION_OUT)
                .setBusUser(UserUtils.currentUser() != null ?UserUtils.currentUser().getName():transfer.getBusUser())
                .setBusUserCode(UserUtils.currentUser() != null ?UserUtils.currentUser().getWorkNo():transfer.getBusUserCode())
                .setBusKey(codeOut)
                .setWarehouseId(transfer.getFromWarehouseId())
                .setWarehouseName(transfer.getFromWarehouseName());
        //将调拨类型作为出入库类型
        if (StringUtils.isNotBlank(transfer.getType()) && GlobalConsts.TransferType.TransferTypeAll.contains(transfer.getType())){
            workOrder.setBusType(transfer.getType());
        }else {
            //同物理仓库之间调拨
            if (transfer.getFromWarehouseId().equals(transfer.getToWarehouseId())){
                workOrder.setBusType(WorkOrderType.OUT_SHIFT_OM);
            }
        }

        workOrder.setFlowStatus(GlobalConsts.FLOW_END_VALUE);//审核完成
        workOrder.setFlowModifier(transfer.getFlowModifier()).setFlowModified(transfer.getFlowModified());
        super.save(workOrder);

        List<WorkOrderItem> workOrderItems = Lists.newArrayListWithCapacity(transferItemList.size());
        transferItemList.forEach(item -> {
            WorkOrderItem workOrderItem = new WorkOrderItem();
            BeanUtils.copyProperties(item, workOrderItem,"id");
            workOrderItem.setWorkOrderId(workOrder.getId());

            //-1 为跟台包，则设置耗材类型为跟台包
            if("-1".equalsIgnoreCase(workOrderItem.getMatrId())) {
                workOrderItem.setMatrType("1");
            }
            workOrderItems.add(workOrderItem);
        });
        workOrderItemService.saveBatch(workOrderItems);
        workOrderMapper.updateWorkOrder(workOrder.getId());
        return workOrder;
    }

    @Override
    protected void beforeHandleFlowChange(FlowContext flowContext, WorkOrder newEntity) {
        super.beforeHandleFlowChange(flowContext, newEntity);
        String buttonKey = flowContext.getButtonKey();

        // 提交审核，不允许空明细提交
        if ("submit".equalsIgnoreCase(buttonKey)) {
            Integer count = workOrderItemService.list(Wrappers.<WorkOrderItem>query().eq("work_order_id", flowContext.getBusId())).size();
            List<WorkOrderItem> outOrderItems = workOrderItemService.list(Wrappers.<WorkOrderItem>query().eq("work_order_id", flowContext.getBusId()));
            if (count == null || count == 0) {
                throw new BusinessException("出库明细为空,请添加后提交");
            }

            outOrderItems.forEach(ob -> {
                //单品数量*单品价格 是否等于 合计金额
                if(UtilNum.mul(ob.getSkuQty(),ob.getSkuPrice()) != ob.getAmount())  {
                    ob.setAmount(UtilNum.mul(ob.getSkuQty(),ob.getSkuPrice()));
                    //单品数量*单品价格 是否等于 合计金额
                    workOrderItemService.updateById(ob);
                }
            });

            final double[] amountDtl = {0};
            outOrderItems.stream().forEach(item -> amountDtl[0] =UtilNum.sum(amountDtl[0],item.getAmount()));
            WorkOrder outOrderDb = this.getById(newEntity.getId());
            if (amountDtl[0] != outOrderDb.getAmount()) {
                outOrderDb.setAmount(amountDtl[0]);
                this.updateById(outOrderDb);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    protected void afterHandleFlowChange(FlowContext flowContext, WorkOrder newEntity) {
        super.afterHandleFlowChange(flowContext, newEntity);

        boolean endNode = flowContext.isEndNode();
        if (!endNode) {
            return;
        }
        checked(flowContext.getBusId());

        // 如果是结束节点，库存出库
        workOrderService.postBatch(Collections.singletonList(flowContext.getBusId()));

        minusStockMatrNum(newEntity);
    }


    //数量化管理 扣减库存
    public void minusStockMatrNum(WorkOrder workOrder) {
        List<WorkOrderItem> outOrderItems = workOrderItemService.list(Wrappers.<WorkOrderItem>query().eq("work_order_id", workOrder.getId()));
        // 数量化管理的耗材id
        List<String> matrIdListMagnum = matrMapper.selectBatchIds(outOrderItems.stream().map(WorkOrderItem::getMatrId).collect(Collectors.toList()))
                .stream().filter(item -> item.getFlagMagnum() != null && item.getFlagMagnum()).map(Matr::getId).collect(Collectors.toList());
        // 因为是调拨加锁的，此处根据调拨明细进行扣减库存
        if (matrIdListMagnum.size() == 0) {
            return;
        }
        List<WorkOrderItem> outOrderItemsNum = outOrderItems.stream().filter(tmp -> matrIdListMagnum.contains(tmp.getMatrId())).collect(Collectors.toList());


        List<String> snList = outOrderItemsNum.stream().map(WorkOrderItem::getSn).collect(Collectors.toList());
        //批量解除实物锁
        stockLockService.unlockBatchWithBusKeyValue(snList,workOrder.getId());

        //扣减库存
        List<Stock> stockList = stockService.list(Wrappers.<Stock>query().in("sn", snList));
        Map<String,WorkOrderItem> transferItemMapBySn = outOrderItemsNum.stream().collect(Collectors.toMap(WorkOrderItem::getSn, t -> t));
        stockList.forEach(stock -> {
            double skuQtyNew = stock.getSkuQty() - transferItemMapBySn.get(stock.getSn()).getSkuQty();
            if (skuQtyNew  < 0) {
                throw  new BusinessException("库存不足");
            }
            stock.setSkuQty(skuQtyNew);
            stock.setPackageQty(stock.getSkuQty());
            stock.setAmount(stock.getSkuPrice() * stock.getSkuQty());
        });
        stockService.updateBatchById(stockList);
    }

    private void checked(String busId) {
        WorkOrder workOrder = this.getById(busId);
        List<WorkOrderItem> outOrderItemList = outOrderItemService.list(Wrappers.<WorkOrderItem>query()
                .select().in("work_order_id", workOrder.getId()));
        List<String> stockIdList = outOrderItemList.stream().map(WorkOrderItem::getStockId).collect(Collectors.toList());
        List<Stock> stockList = stockMapper.selectList(Wrappers.<Stock>query().in("id", stockIdList));
//        分组查询
        Map<WorkOrderKey, List<Stock>> stockMap = stockList.stream().collect(
                Collectors.groupingBy(o -> new WorkOrderKey(o.getSupplierId(), o.getSupplierName(), o.getFlagConsignment(), o.getFlagOmoc())));
        List<Stock> list = stockMap.get(new WorkOrderKey(workOrder.getSupplierId(), workOrder.getSupplierName(), workOrder.getFlagConsignment(), workOrder.getFlagOmoc()));
        if (null == list || list.size()!=outOrderItemList.size()){
            throw new BusinessException("出库单耗材属性和库存不一致,请检查");
        }

    }

    @Override
    public boolean saveOrUpdate(WorkOrder workOrder) {
        if (StringUtils.isBlank(workOrder.getBusKey())) {
            workOrder.setBusKey(serialRuleAdapter.getSerialRuleService().generateCode(SerialKeys.OUT_ORDER_KEY));
        }
        if (workOrder.getDirection() == null) {
            workOrder.setDirection(GlobalConsts.ORDER_DIRECTION_OUT);
        }

        //数据库中是Long类型，不能存空值
        if (StringUtils.isBlank(workOrder.getPurchaseOrderId()))
            workOrder.setPurchaseOrderId(null);
        if (StringUtils.isBlank(workOrder.getInvoiceGroupId()))
            workOrder.setInvoiceGroupId(null);
        if (StringUtils.isBlank(workOrder.getRefId()))
            workOrder.setRefId(null);

        return super.saveOrUpdate(workOrder);
    }

    @Override
    public boolean removeCascadeByIds(Collection<? extends Serializable> idList) {
        //解锁
        List<String> stockIds = outOrderItemService.listObjs(Wrappers.<WorkOrderItem>query()
                .select("stock_id").in("work_order_id", idList), v-> v.toString());
        boolean boolResult = workOrderItemService.remove(new QueryWrapper<WorkOrderItem>().in("work_order_id", idList));
        if (boolResult && stockIds.size() > 0) {
            stockIds.forEach(stockId -> stockLockService.unlock(stockId));
        }
        return this.removeByIds(idList);
    }

    public Stock getByUnicode(String code, String type){
        Stock stock = stockMapper.selectOne(Wrappers.<Stock>query()
        .eq(type,code));
        if(stock == null){
            throw new  BusinessException("未找到唯一码");
        }
        return stock;
    }

    public Map getOutOrder(String code){

        WorkOrder workOrder = baseMapper.selectOne(Wrappers.<WorkOrder>query()
                .eq("bus_key", code));
        if (workOrder == null) {
            throw new BusinessException("该订单未找到！");
        }
        List<WorkOrderItem> workOrderItems = workOrderItemService.list(Wrappers.<WorkOrderItem>query()
                .eq("work_order_id", workOrder.getId()));
        Map result = new HashMap();
        result.put("out", workOrder);
        result.put("outItems", workOrderItems);
        return result;
    }

}
